1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package com.google.common.hash;
27
28 import static com.google.common.primitives.UnsignedBytes.toInt;
29
30 import java.io.Serializable;
31 import java.nio.ByteBuffer;
32 import java.nio.ByteOrder;
33
34 import javax.annotation.Nullable;
35
36
37
38
39
40
41
42
43 final class Murmur3_128HashFunction extends AbstractStreamingHashFunction implements Serializable {
44
45 private final int seed;
46
47 Murmur3_128HashFunction(int seed) {
48 this.seed = seed;
49 }
50
51 @Override public int bits() {
52 return 128;
53 }
54
55 @Override public Hasher newHasher() {
56 return new Murmur3_128Hasher(seed);
57 }
58
59 @Override
60 public String toString() {
61 return "Hashing.murmur3_128(" + seed + ")";
62 }
63
64 @Override
65 public boolean equals(@Nullable Object object) {
66 if (object instanceof Murmur3_128HashFunction) {
67 Murmur3_128HashFunction other = (Murmur3_128HashFunction) object;
68 return seed == other.seed;
69 }
70 return false;
71 }
72
73 @Override
74 public int hashCode() {
75 return getClass().hashCode() ^ seed;
76 }
77
78 private static final class Murmur3_128Hasher extends AbstractStreamingHasher {
79 private static final int CHUNK_SIZE = 16;
80 private static final long C1 = 0x87c37b91114253d5L;
81 private static final long C2 = 0x4cf5ad432745937fL;
82 private long h1;
83 private long h2;
84 private int length;
85
86 Murmur3_128Hasher(int seed) {
87 super(CHUNK_SIZE);
88 this.h1 = seed;
89 this.h2 = seed;
90 this.length = 0;
91 }
92
93 @Override protected void process(ByteBuffer bb) {
94 long k1 = bb.getLong();
95 long k2 = bb.getLong();
96 bmix64(k1, k2);
97 length += CHUNK_SIZE;
98 }
99
100 private void bmix64(long k1, long k2) {
101 h1 ^= mixK1(k1);
102
103 h1 = Long.rotateLeft(h1, 27);
104 h1 += h2;
105 h1 = h1 * 5 + 0x52dce729;
106
107 h2 ^= mixK2(k2);
108
109 h2 = Long.rotateLeft(h2, 31);
110 h2 += h1;
111 h2 = h2 * 5 + 0x38495ab5;
112 }
113
114 @Override protected void processRemaining(ByteBuffer bb) {
115 long k1 = 0;
116 long k2 = 0;
117 length += bb.remaining();
118 switch (bb.remaining()) {
119 case 15:
120 k2 ^= (long) toInt(bb.get(14)) << 48;
121 case 14:
122 k2 ^= (long) toInt(bb.get(13)) << 40;
123 case 13:
124 k2 ^= (long) toInt(bb.get(12)) << 32;
125 case 12:
126 k2 ^= (long) toInt(bb.get(11)) << 24;
127 case 11:
128 k2 ^= (long) toInt(bb.get(10)) << 16;
129 case 10:
130 k2 ^= (long) toInt(bb.get(9)) << 8;
131 case 9:
132 k2 ^= (long) toInt(bb.get(8));
133 case 8:
134 k1 ^= bb.getLong();
135 break;
136 case 7:
137 k1 ^= (long) toInt(bb.get(6)) << 48;
138 case 6:
139 k1 ^= (long) toInt(bb.get(5)) << 40;
140 case 5:
141 k1 ^= (long) toInt(bb.get(4)) << 32;
142 case 4:
143 k1 ^= (long) toInt(bb.get(3)) << 24;
144 case 3:
145 k1 ^= (long) toInt(bb.get(2)) << 16;
146 case 2:
147 k1 ^= (long) toInt(bb.get(1)) << 8;
148 case 1:
149 k1 ^= (long) toInt(bb.get(0));
150 break;
151 default:
152 throw new AssertionError("Should never get here.");
153 }
154 h1 ^= mixK1(k1);
155 h2 ^= mixK2(k2);
156 }
157
158 @Override public HashCode makeHash() {
159 h1 ^= length;
160 h2 ^= length;
161
162 h1 += h2;
163 h2 += h1;
164
165 h1 = fmix64(h1);
166 h2 = fmix64(h2);
167
168 h1 += h2;
169 h2 += h1;
170
171 return HashCode.fromBytesNoCopy(ByteBuffer
172 .wrap(new byte[CHUNK_SIZE])
173 .order(ByteOrder.LITTLE_ENDIAN)
174 .putLong(h1)
175 .putLong(h2)
176 .array());
177 }
178
179 private static long fmix64(long k) {
180 k ^= k >>> 33;
181 k *= 0xff51afd7ed558ccdL;
182 k ^= k >>> 33;
183 k *= 0xc4ceb9fe1a85ec53L;
184 k ^= k >>> 33;
185 return k;
186 }
187
188 private static long mixK1(long k1) {
189 k1 *= C1;
190 k1 = Long.rotateLeft(k1, 31);
191 k1 *= C2;
192 return k1;
193 }
194
195 private static long mixK2(long k2) {
196 k2 *= C2;
197 k2 = Long.rotateLeft(k2, 33);
198 k2 *= C1;
199 return k2;
200 }
201 }
202
203 private static final long serialVersionUID = 0L;
204 }